﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Sinacor.OMM.ConnectionManager;
using Sinacor.OMM.HealthMonitor.Collector;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.HftProcessor;
using Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.Router;
using Sinacor.OMM.ConnectionManager.InternalQueue;
using Sinacor.OMM.Util;
using PSE.Framework.Threading;

namespace Sinacor.OMM.Router.ProcessLogic
{
    public class MessageReader : IDisposable
    {
        private TraceWriter _traceWriter;

        /// <summary>
        /// 
        /// </summary>
        public event ProcessEntity.RouterEventHandler OnError;

        /// <summary>
        /// 
        /// </summary>
        public const string ROUTER_MESSAGE_READER_QUEUE_ID = "RouterMessageReader";

        /// <summary>
        /// 
        /// </summary>
        private Translator.Interfaces.ITranslator _translator;

        /// <summary>
        /// 
        /// </summary>
        private object _lock = new object();

        /// <summary>
        /// 
        /// </summary>
        private ConnectionManager.MSMQ.MSMQHelper _msmqHelper;

        /// <summary>
        /// 
        /// </summary>
        private Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver _receiver;

        /// <summary>
        /// 
        /// </summary>
        private RouterShdms _routerShdms;

        /// <summary>
        /// 
        /// </summary>
        private DateTime _lastHealthStatusChecked;

        /// <summary>
        /// 
        /// </summary>
        private int _totalTranslatedMessagesPerInterval = 0;

        /// <summary>
        /// 
        /// </summary>
        private InternalQueueManager<MessageEntity.BaseMessage> _internalQueueManager;

        /// <summary>
        /// 
        /// </summary>
        private bool _shouldStopCollecting;

        /// <summary>
        /// 
        /// </summary>
        private DumpClient.Dump _dumpClient;

        public MessageReader()
        {
            _dumpClient = DumpClient.Dump.GetInstance();
            // Router statistics
            _routerShdms = RouterShdms.GetSharedStorage();
            _routerShdms.RouterDataInfo.MessageReceiverInfo.LimitInternalQueuedMessages_01_Quantity = 200000;

            _lastHealthStatusChecked = new DateTime(1, 1, 1);
            // _traceWriter
            _traceWriter = Tracer.GetInstance("Roteador", "MessageReader.log");

            _msmqHelper = new Sinacor.OMM.ConnectionManager.MSMQ.MSMQHelper();
            _internalQueueManager = ConnectionManager.InternalQueue.InternalQueueManager<MessageEntity.BaseMessage>.GetInstance();

            // collectorThread
            Thread collectorThread = new Thread(new ThreadStart(Collect));
            collectorThread.Start();
        }

        private void Collect()
        {
            while (!_shouldStopCollecting)
            {
                _routerShdms.RouterDataInfo.MessageReceiverInfo.TranslatedMessagesForSecond = CalculateTranslatedMessagesPerSecond();
                _routerShdms.RouterDataInfo.MessageReceiverInfo.InternalQueuedMessages_01_Quantity = _internalQueueManager.Count(ROUTER_MESSAGE_READER_QUEUE_ID);
                Sleeper.Sleep(1000, () => !_shouldStopCollecting);
            }
        }

        private int CalculateTranslatedMessagesPerSecond()
        {
            int seconds;
            int translatedMessagesPerSecond;

            if (_lastHealthStatusChecked.Year == 1)
                _lastHealthStatusChecked = DateTime.Now;

            // Calcula o número de segundos entre a última coleta e a coleta atual
            seconds = DateTime.Now.Subtract(_lastHealthStatusChecked).Seconds;

            if (seconds == 0)
                seconds = 1;

            if (_totalTranslatedMessagesPerInterval >= seconds)
                translatedMessagesPerSecond = _totalTranslatedMessagesPerInterval / seconds;
            else
                translatedMessagesPerSecond = _totalTranslatedMessagesPerInterval;

            // Zera o contador que será usado na próxima coleta
            // Esse contador é incrementado dentro do Run()
            // a cada mensagem recebida
            _totalTranslatedMessagesPerInterval = 0;

            // Define o horário dessa verificação, para ser usada na próxima chamada da função e calcular os segundos entre uma chamada e outra
            _lastHealthStatusChecked = DateTime.Now;

            return translatedMessagesPerSecond;
        }        

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public void StartReader()
        {
            try
            {
                _receiver = new Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver(
                    RouterConfigurationManager.RouterConfiguration.MessageMainRepository.ConnectionInformation["CONNECTION_STRING"]);
                _receiver.MessageReceived += new Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver.MessageArrival(_receiver_MessageReceived);
                _receiver.OnHealthStatusChanged += new Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver.HealthStatusChanged(_receiver_OnHealthStatusChanged);
                _traceWriter.Trace("StartReader: The reading has been started.");
            }
            catch (Exception ex)
            {
                _traceWriter.Trace(string.Concat("Error when trying to start the MessageReader: ", ex.Message, ex.StackTrace));
                HandleError(string.Concat("Error when trying to start the MessageReader: ", ex.Message, ex.StackTrace));
            }
        }

        void _receiver_MessageReceived(System.Messaging.Message message, PSE.Framework.Messaging.MessageReceivedEventArgs e)
        {
            MessageEntity.BaseMessage baseMessage;

            try
            {
                // gets the translator
                _translator = Translator.TranslatorFactory.GetTranslator(message.Label,
                    message.Body.ToString(), Sinacor.OMM.Translator.Enum.ProtocolTypes.STM);

                if (_translator != null)
                {
                    _traceWriter.Trace(string.Concat("The message ", message.Label, " will be translated."), Sinacor.OMM.Util.TraceType.Optional);
                    baseMessage = _translator.TranslateToRouterMessage();
                    _traceWriter.Trace(string.Concat("The message ", message.Label, " has been translated."), Sinacor.OMM.Util.TraceType.Optional);

                    _traceWriter.Trace(string.Concat("The message ", message.Label, " will be enqueued."), Sinacor.OMM.Util.TraceType.Optional);
                    _internalQueueManager.Enqueue(ROUTER_MESSAGE_READER_QUEUE_ID, baseMessage);
                    _traceWriter.Trace(string.Concat("The message ", message.Label, " has been enqueued."), Sinacor.OMM.Util.TraceType.Optional);
                }

                _totalTranslatedMessagesPerInterval++;

                e.Result = true;
            }
            catch (Exception ex)
            {
                StringBuilder recoveryProcedure = new StringBuilder();

                recoveryProcedure.Append(@"Parar o roteador \r\n");
                recoveryProcedure.Append(@"Inserir a mensagem manualmente na fila de banco. Se a mensagem começar com '<AN', inserí-la na tabela TORHUB_TXT, caso contrário inserí-la na tabela TORSINL_OFER \r\n");
                recoveryProcedure.Append(@"Aguardar o término do processamento de todas as mensagens de todos os processos HFT e do fluxo normal. Isso pode ser verificado através do monitor.  \r\n");
                recoveryProcedure.Append(@"Executar os seguintes updates, onde <MESSAGE_ID> é o id da mensagem que foi inserida manualmente na fila de banco. \r\n");
                recoveryProcedure.Append(@"UPDATE TBOPROS_IAFR SET COD_ULT_MSG_PROS = <MESSAGE_ID> \r\n");
                recoveryProcedure.Append(@"UPDATE TORSLESEQ SET COD_ULT_MSG_PROS = <MESSAGE_ID> \r\n");
                recoveryProcedure.Append(@"- Iniciar o roteador novamente \r\n");

                _routerShdms.RouterDataInfo.ErrorData = new Sinacor.OMM.HealthMonitor.Entity.Common.ErrorData
                {
                    Source = "Roteador",
                    ErrorDescription = "Error when translating the message.",
                    ExceptionMessage = ex.Message,
                    BusinessMessage = message.Body.ToString(),
                    ProcedureToTake = recoveryProcedure.ToString()
                };

                _traceWriter.Trace(string.Concat("Error when translating the message [", message.Body.ToString(), "]", ex.Message, ex.StackTrace));
                HandleError(string.Concat("Error when translating the message [", message.Body.ToString(), "]", ex.Message, ex.StackTrace));

                Pause();
                e.Result = false;
            }
        }

        void _receiver_OnHealthStatusChanged(Sinacor.OMM.ConnectionManager.MSMQ.MsmqReceiver msmqReceiver)
        {
            //MSMQ
            _routerShdms.RouterDataInfo.MessageReceiverInfo.MsmqConnectionsRetries = msmqReceiver.TotalMsmqConnectionTries;
            _routerShdms.RouterDataInfo.MessageReceiverInfo.MessagesReceivedQuantity = msmqReceiver.MessagesReceivePerSecond;
            _routerShdms.RouterDataInfo.MsmqRouter.LimitQueueAcessible = true;
            _routerShdms.RouterDataInfo.MsmqRouter.QueueAcessible = msmqReceiver.IsConnected;
            _routerShdms.RouterDataInfo.MsmqRouter.QueuedMessages = msmqReceiver.QueuedMessages;
            _routerShdms.RouterDataInfo.MsmqRouter.QueueBytesSize = msmqReceiver.QueueSizeBytes;
            _routerShdms.RouterDataInfo.MsmqRouter.MessagesPerSecond = msmqReceiver.MessagesReceivePerSecond;
            _routerShdms.RouterDataInfo.MsmqRouter.QueuePath = msmqReceiver.Path;
        }

        public void Pause()
        {
            try
            {
                _receiver.PauseMsmqReceiver();
            }
            catch (Exception ex)
            {
                _traceWriter.Trace(string.Concat("Error when trying to pause the MsmqReceiver: ", ex.Message, ex.StackTrace));
                HandleError(string.Concat("Error when trying to pause the MsmqReceiver: ", ex.Message, ex.StackTrace));
            }
        }

        public void Resume()
        {
            try
            {
                _receiver.ResumeMsmqReceiver();
            }
            catch (Exception ex)
            {
                _traceWriter.Trace(string.Concat("Error when trying to resume the MsmqReceiver: ", ex.Message, ex.StackTrace));
                HandleError(string.Concat("Error when trying to resume the MsmqReceiver: ", ex.Message, ex.StackTrace));
            }
        }

        /// <summary>
        /// Recycles the configuration of the message reader.
        /// </summary>
        public void Recycle()
        {
            lock (_lock)
            {
                _lastHealthStatusChecked = new DateTime(1, 1, 1);
                _totalTranslatedMessagesPerInterval = 0;
            }
        }

        private void HandleError(string message)
        {
            if (OnError != null)
                OnError(this, new ProcessEntity.RouterEventArgs() { ErrorMessage = message });
        }

        #region IDisposable Members

        public void Dispose()
        {
            _shouldStopCollecting = true;
            _msmqHelper = null;

            if (_internalQueueManager != null)
                _internalQueueManager.Dispose();

            if (_receiver != null)
                _receiver.Dispose();
            _receiver = null;

            _traceWriter = null;
        }

        #endregion
    }
}
